//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<ResourceLoader.cpp>
///	@path	~/src/scene/resource/
///	@date	2008/04/11
///	@desc	.

#define _FULLAPI
#include "config/config.h"

#include "lib/utilities/callback.h"

#include "gui/image/CColorBitmap.h"
#include "gui/image/CSpriteBitmap.h"

#include "scene/resource/ResourceLoader.h"
#include "scene/loader/CGeometryLoader.h"
#include "scene/loader/CModelLoader.h"

namespace xeres {

	// ResourceLoader
	ResourceLoader::ResourceLoader( void )
	{

	}
	// ~ResourceLoader
	ResourceLoader::~ResourceLoader( void )
	{

	}

	namespace
	{
		struct _ImageLoader
		{
		public:
			/// ctor
			_ImageLoader( ResourceLoader * loader )
				: m_loader( loader )
			{
			}
			
			RefPass<IBitmap> operator() ( const WString& path , int count , int starts )
			{
				StringSeq lst;
				break_string( path , ';' , lst );

				using namespace Gdiplus;
				if( ( count == 1 || count == 0 ) && lst.size() <= 1 )
				{
					IStream * stream = m_loader->OpenIStream( path );
					if( !stream )
					{
						return NULL;
					}

					Bitmap bitmap( stream , FALSE );
					::Sleep(0);
					stream->Release();

					size_t width = bitmap.GetWidth();
					size_t height = bitmap.GetHeight();
					if( width == 0 || height == 0 )
					{
						return NULL;
					}

					RefPass<IBitmap> bmp = new CColorBitmap( &bitmap );
					return bmp;
				}
				else if( path.find( _S("%d") ) != WString::npos && count > 1 )
				{
					std::vector< Gdiplus::Bitmap * > ani( count );
					for( int i = 0 ; i < count ; ++i )
						ani[i] = NULL;

					for( int i = 0 ; i < count ; ++i )
					{
						wchar_t seq[OS_MAX_PATH];
						swprintf( seq , path.c_str() , starts + i );

						IStream * stream = m_loader->OpenIStream( seq );
						if( !stream )
						{
							for( int j = 0 ; j < i ; ++j )
								delete ani[j];
							return NULL;
						}
						ani[i] = new Bitmap( stream );
						::Sleep(0);
						stream->Release();
					}

					RefPass<IBitmap> sprite = new CSpriteBitmap( ani );
					for( int i = 0 ; i < count ; ++i )
					{
						delete ani[i];
					}
					return sprite;
				}
				else
				{
					std::vector< Gdiplus::Bitmap * > ani( lst.size() );
					for( size_t i = 0 , total = lst.size() ; i < total ; ++i )
						ani[i] = NULL;

					for( size_t i = 0 , total = lst.size() ; i < total ; ++i )
					{
						IStream * stream = m_loader->OpenIStream( lst[i] );
						if( !stream )
						{
							for( size_t j = 0 ; j < i ; ++j )
								delete ani[j];
							return NULL;
						}
						ani[i] = new Bitmap( stream );
						::Sleep(0);
						stream->Release();
					}

					RefPass<IBitmap> sprite = new CSpriteBitmap( ani );
					for( size_t i = 0 , total = lst.size() ; i < total ; ++i )
					{
						delete ani[i];
					}
					return sprite;
				}
			}

		private:

			ResourceLoader *	m_loader;
		};

		struct _ImageGifLoader
		{
		public:
			/// ctor
			_ImageGifLoader( ResourceLoader * loader )
				: m_loader( loader )
			{
			}
			
			RefPass<IBitmap> operator() ( const WString& path , int count , int starts )
			{
				using namespace Gdiplus;
				IStream * stream = m_loader->OpenIStream( path );
				if( !stream )
				{
					return NULL;
				}

				Bitmap bitmap( stream , FALSE );
				::Sleep(0);

				size_t width = bitmap.GetWidth();
				size_t height = bitmap.GetHeight();
				if( width == 0 || height == 0 )
				{
					return NULL;
				}

				count = bitmap.GetFrameDimensionsCount();
				GUID * pDimensionIDs = (GUID*)new ( alloca( sizeof(GUID)*count) ) GUID[count];
				bitmap.GetFrameDimensionsList( pDimensionIDs , count );
				WCHAR strGuid[39];
				StringFromGUID2( pDimensionIDs[0] , strGuid , 39 );
				UINT frameCount = bitmap.GetFrameCount( &pDimensionIDs[0] );

				if( frameCount <= 1 )
				{
					RefPass<IBitmap> bmp = new CColorBitmap( &bitmap );
					return bmp;
				}
				else
				{
					RefPass<IBitmap> bmp = new CGifBitmap( bitmap );
					return bmp;
				}
			}

		private:

			ResourceLoader *	m_loader;
		};
	}

	// Initialize
	void ResourceLoader::Initialize( Dispatcher * dispatcher , RenderManager * renderman )
	{
		ResourceProvider::Initialize( renderman );
		m_dispatcher = dispatcher;

		// register default loaders
		RegisterGeometryLoader( _S("simple") , new CSimpleGeometryLoader );
		RegisterGeometryLoader( _S("general") , new CGeometryLoader );

		RegisterModelLoader( _S("simple") , new CSimpleModelLoader );

		RegisterLoader( _S("png") , _ImageLoader( this ) );
 		RegisterLoader( _S("jpg") , _ImageLoader( this ) );
 		RegisterLoader( _S("jpeg") , _ImageLoader( this ) );
		RegisterLoader( _S("bmp") , _ImageLoader( this ) );
		RegisterLoader( _S("tif") , _ImageLoader( this ) );
		RegisterLoader( _S("tiff") , _ImageLoader( this ) );
		RegisterLoader( _S("gif") , _ImageGifLoader( this ) );

		// register effect loader for render manager
		renderman->RegisterEffectLoader( _S("ResourceLoader::LoadEffect") , bind( &ResourceLoader::LoadEffectFromData , this , _1 , _2 ) );

		// FIXME: should add effect prefix modification
		m_effectPrefix = _S("res/fx/");
	}
	// Finalize
	void ResourceLoader::Finalize( void )
	{
		m_geometryLoaders.clear();
		m_modelLoaders.clear();
		ResourceProvider::Finalize();
	}
	// RegisterGeometryLoader
	bool ResourceLoader::RegisterGeometryLoader( const WString& name , RefWeak<IGeometryLoader> loader )
	{
		if( m_geometryLoaders.has_key( name ) )
		{
			TRACE_ERROR( _S("ResourceLoader::RegisterGeometryLoader: Loader '%s' has been registered.") , name.c_str() );
			return false;
		}

		m_geometryLoaders.insert( name , loader );
		return false;
	}
	// RegisterModelLoader
	bool ResourceLoader::RegisterModelLoader( const WString& name , RefWeak<IModelLoader> loader )
	{
		if( m_modelLoaders.has_key( name ) )
		{
			TRACE_ERROR( _S("ResourceLoader::RegisterModelLoader: Loader '%s' has been registered.") , name.c_str() );
			return false;
		}

		m_modelLoaders.insert( name , loader );
		return false;
	}
	// LoadGeometry
	RefPass<IGeometry> ResourceLoader::LoadGeometry( RefWeak<IData> res )
	{
		TRACK_FUNCTION_SCOPE();

		WString loaderName;
		if( !res->GetAttribute( _S("loader") , loaderName ) )
		{
			TRACE_ERROR( _S("ResourceLoader::LoadGeometry: Geometry loader is not specified.") );
			return NULL;
		}

		if( !m_geometryLoaders.has_key( loaderName ) )
		{
			TRACE_ERROR( _S("ResourceLoader::LoadGeometry: Cannot find geometry loader '%s'.") , loaderName.c_str() );
			return NULL;
		}

		return m_geometryLoaders.find( loaderName )->second->OnLoad( res , *this );
	}
	// LoadModel
	RefPass<IModel> ResourceLoader::LoadModel( RefWeak<IData> res )
	{
		TRACK_FUNCTION_SCOPE();

		WString loaderName;
		if( !res->GetAttribute( _S("loader") , loaderName ) )
		{
			TRACE_ERROR( _S("ResourceLoader::LoadModel: Model loader is not specified.") );
			return NULL;
		}

		if( !m_modelLoaders.has_key( loaderName ) )
		{
			TRACE_ERROR( _S("ResourceLoader::LoadModel: Cannot find model loader '%s'.") , loaderName.c_str() );
			return NULL;
		}

		return m_modelLoaders.find( loaderName )->second->OnLoad( res , *this );
	}

	// LoadEffect
	RefPass<IEffect> ResourceLoader::LoadEffectFromData( const WString& url , RefWeak<IEffectPool> pool )
	{
		Ref<IBuffer> buffer = LoadBinaryFromFile( m_effectPrefix + url );
		if( buffer.IsValid() )
		{
 			Ref<IEffect> effect = Renderer()->CreateEffect( buffer->GetData() , buffer->GetSize() , pool );
			return effect;
		}
		return NULL;
	}

} // namespace xeres
